package com.storage.web.controller.system;

import com.storage.common.annotation.Log;
import com.storage.common.core.controller.BaseController;
import com.storage.common.core.domain.AjaxResult;
import com.storage.common.core.domain.ServiceResponse;
import com.storage.common.enums.BusinessType;
import com.storage.common.enums.UserAndAgentLevelType;
import com.storage.common.jpush.JiGuangPushUtil;
import com.storage.common.utils.ServletUtils;
import com.storage.common.utils.StringUtils;
import com.storage.framework.jwt.Constant;
import com.storage.framework.jwt.CustomizedToken;
import com.storage.framework.shiro.service.StoragePasswordService;
import com.storage.framework.util.JwtUtil;
import com.storage.framework.util.ShiroUtils;
import com.storage.framework.web.OperationContext;
import com.storage.framework.web.OperationContextHolder;
import com.storage.framework.web.enums.RequestOperation;
import com.storage.system.domain.StorageIndustry;
import com.storage.system.domain.StorageNoticeMessage;
import com.storage.system.domain.StorageUser;
import com.storage.system.service.*;
import com.storage.system.vo.StorageUserVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * 登录验证
 *
 * @author hzhang7
 */
@Api(tags = "登录相关接口")
@RestController
public class StorageLoginController extends BaseController {

    private static final Logger logger = LoggerFactory.getLogger(StorageLoginController.class);

    @Autowired
    private IStorageUserService storageUserService;

    @Autowired
    private StoragePasswordService passwordService;

    @Autowired
    private IStorageUserAgentService storageUserAgentService;

    @Autowired
    private ISmsService smsService;

    @Autowired
    private IStorageIndustryService storageIndustryService;

    @Autowired
    private IStorageMessageService storageMessageService;

    @Autowired
    private IRedisService redisService;

    @GetMapping("/api/login")
    public String login(HttpServletRequest request, HttpServletResponse response) {
        return ServletUtils.renderString(response, "{\"code\":\"1\",\"msg\":\"未登录或登录超时。请重新登录\"}");
    }

    @ApiOperation(value = "根据账号密码登陆", notes = "根据用户名/密码实现登陆")
    @PostMapping("/api/logout")
    @ResponseBody
    public AjaxResult logout(HttpServletRequest request) {
        OperationContextHolder.clear();
        return null;
    }

    @ApiOperation(value = "根据账号密码登陆", notes = "根据用户名/密码实现登陆")
    @PostMapping("/api/login")
    @ResponseBody
    public AjaxResult ajaxLogin(HttpServletRequest request, HttpServletResponse response, @RequestBody StorageUserVO user) {
        String username = user.getPhone();
        String password = user.getPassword();
        // 登录模式：pc or mobile
        String type = request.getHeader(Constant.TYPE_HEADER_NAME);

        logger.info("username={};password={};rememberMe={}", username, password);
        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
            return error(String.format("用户名或密码为空: {\"username\":\"%s\",\"password\":\"%s\"}", username, password));
        }

        CustomizedToken token = new CustomizedToken(username, password, "UserRealm");

        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);
            StorageUser storageUser = storageUserService.selectUserByPhoneNumber(username);
            // 生成jwtToken
            String jwtToken = JwtUtil.sign(storageUser.getPhone(), storageUser.getId(), storageUser.getPassword(), type);
            // 设置头信息
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/json; charset=utf-8");
            response.addHeader(Constant.TOKEN_HEADER_NAME, jwtToken);
            // 将用户信息存入 ThreadLocal
            OperationContextHolder.clear();
            OperationContextHolder.set(new OperationContext(storageUser));
            // 将 token 存入 redis (查询缓存中是否已有该账号的token缓存)
            redisService.setKey(jwtToken, jwtToken, Constant.TOKEN_EXPIRE_TIME);
            storageUser.setAuthorization(jwtToken);
            return AjaxResult.success(storageUser);
        } catch (AuthenticationException e) {
            e.printStackTrace();
            String msg = "用户或密码错误";
            if (StringUtils.isNotEmpty(e.getMessage())) {
                msg = e.getMessage();
            }
            return error(msg);
        }
    }

    @ApiOperation(value = "微信登陆", notes = "微信登陆获取openid、头像、昵称等信息")
    @PostMapping("/wx/login")
    @ResponseBody
    public AjaxResult wxLogin(String code) {
        if (org.apache.commons.lang3.StringUtils.isBlank(code)) {
            return error("微信code为空");
        }
        StorageUser storageUser = storageUserService.selectStorageUserByWxCode(code);
        if (storageUser == null) {
            return error("微信授权登陆失败");
        }

        return AjaxResult.success("授权登录", storageUser);
    }

    @ApiOperation(value = "获取当前登陆用户", notes = "获取当前登陆用户")
    @GetMapping("/api/current")
    @ResponseBody
    public AjaxResult getCurrentUser(HttpServletRequest request,
                                     @RequestOperation OperationContext context) {
        String phone = null;
        StorageUser storageUser = null;
        String token = request.getHeader(Constant.TOKEN_HEADER_NAME);
        if (token == null) {
            return AjaxResult.error("请携带token访问");
        } else if ((phone = JwtUtil.getPhone(token)) == null) {
            return AjaxResult.error("不存在与token关联的用户");
        } else if ((storageUser = storageUserService.selectUserByPhoneNumber(phone)) == null) {
            return AjaxResult.error("用户不存在");
        }

        return AjaxResult.success(storageUser);
    }


    /**
     * 用户注册
     */
    @ApiOperation(value = "用户注册", notes = "新增保存用户")
    @Log(title = "用户", businessType = BusinessType.INSERT)
    @PostMapping("/api/register")
    @ResponseBody
    public AjaxResult addSave(@RequestOperation OperationContext context, @RequestBody StorageUserVO storageUserVO) {
        String error = null;
        StorageUser agentUser = null;
        ServiceResponse<Boolean> response = null;
        if (StringUtils.isEmpty(storageUserVO.getIdentifyCode())) {
            error = "验证码不能为空";
        } else if (StringUtils.isEmpty(storageUserVO.getPhone())) {
            error = "电话号码不能为空";
        } else if (!(response = smsService.checkIsCorrectCode(storageUserVO.getPhone(), storageUserVO.getIdentifyCode())).isSuccess()) {
            error = response.getError();
        } else if (StringUtils.isNotEmpty(storageUserVO.getInvitationCode())
                && (agentUser = storageUserService.selectUserByPhoneNumber(storageUserVO.getInvitationCode())) == null) {
            error = "无效的邀请码";
        } else if (storageUserService.selectUserByPhoneNumber(storageUserVO.getPhone()) != null) {
            error = "当前电话已经注册";
        } else if (StringUtils.isNotEmpty(storageUserVO.getBusinessCode())
                && (storageUserService.selectUserByBusinessCode(storageUserVO.getBusinessCode()) == null)) {
            error = "无效的业务员编码";
        }

        if (StringUtils.isNotBlank(error)) {
            return AjaxResult.error(error);
        }

        // 数据流转
        StorageUser currentRegisterUser = storageUserVO.toRegisterDO(storageUserVO);
        // 设置随机盐
        currentRegisterUser.setSalt(ShiroUtils.randomSalt());
        // 构建密码
        String password = passwordService.encryptPassword(currentRegisterUser.getPhone(), currentRegisterUser.getPassword(), currentRegisterUser.getSalt());
        logger.warn("password={}", password);
        currentRegisterUser.setPassword(password);

        // 插入用户
        storageUserService.insertStorageUser(context.getStorageUser(), currentRegisterUser);
        if (currentRegisterUser.getId() != null && StringUtils.isNotEmpty(storageUserVO.getInvitationCode())) {
            // 持久化消息记录
            String title = String.format("%s 成为你的下级代理商", currentRegisterUser.getNickName());

            String phone = agentUser.getPhone();
            Long userId = agentUser.getId();
            StorageNoticeMessage noticeMessage = new StorageNoticeMessage(userId, phone, null, title);
            ServiceResponse<StorageNoticeMessage> responseNotice = storageMessageService.insertNoticeMessage(noticeMessage);
            if (responseNotice.isSuccess()) {
                // 给上级插入系统消息并极光推送
                JiGuangPushUtil.pushNotice(phone, title, null, responseNotice.getData().getId());
            }

            // 组建上下级关系 - 当前用户是代理商的下级;代理商使用户的上级
            storageUserAgentService.addUserAndAgentRelationship(currentRegisterUser.getId(), agentUser.getId(), UserAndAgentLevelType.UP.getValue(), null);
        }
        return AjaxResult.success(storageUserService.selectStorageUserById(currentRegisterUser.getId()));
    }

    /**
     * 发送短信验证码
     *
     * @param mobile
     * @return
     */
    @ApiOperation(value = "发送短信验证码", notes = "发送短信验证码")
    @RequestMapping("/api/sendSms")
    public AjaxResult sendMessage(@RequestParam String mobile) {
        return smsService.sendMessage(mobile);
    }

    /**
     * 判断验证码是否正确
     *
     * @param mobile
     * @param identifyCode
     * @return
     */
    @RequestMapping("/api/checkIsCorrectCode")
    public AjaxResult checkIsCorrectCode(@RequestParam String mobile, @RequestParam String identifyCode) {
        ServiceResponse<Boolean> response = smsService.checkIsCorrectCode(mobile, identifyCode);
        return response.isSuccess() ? AjaxResult.success() : AjaxResult.error(response.getError());
    }

    /**
     * 获取已有行业列表信息
     *
     * @return
     */
    @ApiOperation(value = "获取已有行业列表信息", notes = "获取已有行业列表信息")
    @Log(title = "用户", businessType = BusinessType.OTHER)
    @GetMapping("/api/industry/list")
    @ResponseBody
    public AjaxResult getIndustryList(StorageIndustry storageIndustry) {
        ServiceResponse<List<StorageIndustry>> response = storageIndustryService.selectStorageIndustryList(storageIndustry);
        return AjaxResult.success(response.getData());
    }

    /**
     * 重置密码
     * tip：无论手机端或者web端任意一方重置了密码，立即失效两个端的token
     *
     * @param mobile
     * @param identifyCode
     * @param newPassword
     * @return
     */
    @ApiOperation(value = "重置密码", notes = "重置密码")
    @PostMapping("/api/reset/password")
    @ResponseBody
    public AjaxResult restPassword(@RequestOperation OperationContext context,
                                   @RequestParam String mobile,
                                   @RequestParam String identifyCode,
                                   @RequestParam String newPassword) {
        if (!smsService.checkIsCorrectCode(mobile, identifyCode).isSuccess()) {
            return AjaxResult.error("验证码错误");
        }
        StorageUser currentRegisterUser = storageUserService.selectUserByPhoneNumber(mobile);
        if (currentRegisterUser == null) {
            return AjaxResult.error("当前手机号未注册");
        }
        // 修改密码
        // 设置随机盐
        currentRegisterUser.setSalt(ShiroUtils.randomSalt());
        // 构建密码
        String password = passwordService.encryptPassword(currentRegisterUser.getPhone(), newPassword, currentRegisterUser.getSalt());
        logger.warn("password={}", password);
        currentRegisterUser.setPassword(password);
        ServiceResponse<StorageUser> response = storageUserService.updateStorageUser(context.getStorageUser(), currentRegisterUser);
        return AjaxResult.success(response.getData());
    }

}
